--
-- Stored Milliseconds.
--
local CattleProdTimer_Millis = 0;

--
-- Listeners array.
--
-- { ID, INTERVAL, ITERATIONS, LISTENER, SECONDS }
--
local CattleProdTimer_Listeners = {};

--
-- Constant: Id
--
local CattleProdTimer_ID = 1;

--
-- Constant: Interval
--
local CattleProdTimer_INTERVAL = 2;

--
-- Constant: Iterations
--
local CattleProdTimer_ITERATIONS = 3;

--
-- Constant: Listener
--
local CattleProdTimer_LISTENER = 4;

--
-- Constant: Seconds
--
local CattleProdTimer_SECONDS = 5;

--
-- Timer OnUpdate listener.
--
function CattleProdTimer_OnUpdate(self, elapsedTime)
	CattleProdTimer_UpdateMillis(elapsedTime);
end

--
-- Update millis.
--
-- elapsedTime Elapsed Time (in seconds)
--
function CattleProdTimer_UpdateMillis(elapsedTime)
	CattleProd_AssertType(elapsedTime, "number");

	-- update millis with elapsed time
	CattleProdTimer_Millis = CattleProdTimer_Millis + elapsedTime;
	if (CattleProdTimer_Millis < 1)
	then
		-- greater than second granulatiry
		return;
	end

	-- set seconds and update millis
	local seconds = math.floor(CattleProdTimer_Millis);
	CattleProdTimer_Millis = seconds - math.floor(CattleProdTimer_Millis);

	for id, listenerObject in pairs(CattleProdTimer_Listeners)
	do
		-- update time
		CattleProdTimer_UpdateListener(listenerObject, seconds);

		-- call listener
		CattleProdTimer_CallListener(listenerObject);

		-- cleanup used up listener
		local iterations = listenerObject[CattleProdTimer_ITERATIONS];
		if (iterations == 0)
		then
			CattleProdTimer_RemoveListener(id);
		end
	end
end

--
-- Add listener to listeners array.
--
-- interval Interval (seconds)
-- iterations Iterations (-1 = infinite)
-- listener Function
--
function CattleProdTimer_AddListener(id, interval, iterations, listener)
	CattleProd_AssertNotNil(id, "Illegal Argument");
	CattleProd_AssertType(interval, "number", "Illegal Argument");
	CattleProd_AssertType(iterations, "number", "Illegal Argument");
	CattleProd_AssertType(listener, "function", "Illegal Argument");

	CattleProd_Debug("AddListener: %s, %i, %i", id, interval, iterations);

	CattleProdTimer_Listeners[id] = { id, interval, iterations, listener, interval };
end

--
-- Remove listener.
--
-- id Id
--
function CattleProdTimer_RemoveListener(id)
	CattleProd_AssertNotNil(id, "Illegal Argument");

	CattleProd_Debug("RemoveListener: %s", id);

	CattleProdTimer_Listeners[id] = nil;
end

--
-- Check if listener is enabled.
--
-- id Listener Id
--
-- return Boolean
--
function CattleProdTimer_IsListener(id)
	CattleProd_AssertNotNil(id, "Illegal Argument");

	local listenerObject = CattleProdTimer_Listeners[id];

	return (listenerObject ~= nil);
end

--
-- Update listener seconds.
--
-- listenerObject Listener Object
-- seconds Seconds
--
function CattleProdTimer_UpdateListener(listenerObject, seconds)
	CattleProd_AssertType(listenerObject, "table", "Illegal Argument");
	CattleProd_AssertType(seconds, "number", "Illegal Argument");

	-- update seconds
	listenerObject[CattleProdTimer_SECONDS] = listenerObject[CattleProdTimer_SECONDS] - seconds;
end

--
-- Update listenerObject seconds and call listener.
--
-- listenerObject Listener Object
--
function CattleProdTimer_CallListener(listenerObject)
	CattleProd_AssertType(listenerObject, "table", "Illegal Argument");

	if (listenerObject[CattleProdTimer_SECONDS] < 1)
	then
		-- update iterations
		local iterations = listenerObject[CattleProdTimer_ITERATIONS];
		if (iterations > 0)
		then
			-- decrement iterations counter
			iterations = iterations - 1;
			listenerObject[CattleProdTimer_ITERATIONS] = iterations;
		end

		-- update seconds
		local interval = listenerObject[CattleProdTimer_INTERVAL];
		local seconds = listenerObject[CattleProdTimer_SECONDS];

		listenerObject[CattleProdTimer_SECONDS] = (interval - seconds);

		local id = listenerObject[CattleProdTimer_ID];

		CattleProd_Debug("CallListener: %s, %d, %d", id, iterations, interval);

		-- call listener
		local listener = listenerObject[CattleProdTimer_LISTENER];
		listener();
	end
end

--
-- Get remaining seconds.
--
-- id Listener Id
--
-- return Seconds
--
function CattleProdTimer_GetSeconds(id)
	CattleProd_AssertNotNil(id, "Illegal Argument");

	local listenerObject = CattleProdTimer_Listeners[id];
	local seconds = listenerObject[CattleProdTimer_SECONDS];

	return seconds;
end
